package com.example.routeplansystem.algorithm.imp;

import com.alibaba.fastjson.JSON;
import com.example.routeplansystem.algorithm.Algorithm;
import com.example.routeplansystem.entity.po.FinalSolution;
import com.example.routeplansystem.entity.po.NodePojo;
import com.example.routeplansystem.entity.po.Solution;
import com.example.routeplansystem.entity.po.VehicleMessage;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.rmi.ServerException;
import java.util.*;

/**
 * @Author lucong
 * @Date 2022/5/13
 * @Description
 */
@Component
public class LKHAlgorithm implements Algorithm {

    @Value("${algorithm.LKH.path}")
    private String path;

    private static String[] config = {
            "PATCHING_A = 2",
            "MOVE_TYPE = 5",
            "PATCHING_C = 3",
    };

    @Override
    public List<Solution> getSolution(List<NodePojo> nodes, List<NodePojo> centerNodes, List<VehicleMessage> vehicles, FinalSolution finalSolution) throws ServerException {
        int finalSolutionId = finalSolution.getFinalSolutionId();
        NodePojo center = centerNodes.get(0);//只能处理一个中心点
        nodes.add(0, center); // 将中心点放在最前面
        generateParFile(finalSolutionId);
        generateProblemFile(nodes, finalSolutionId);
        runLKH(finalSolutionId);
        VehicleMessage vehicle = vehicles.get(0);// 只能处理一辆车
        List<Solution> res = readResultFile(nodes, vehicle, finalSolutionId);
        return res;
    }

    @Override
    public void checkMatrix(Integer questionId,Integer finalSolutionId) {

    }

    /**
     * 构建par参数文件
     *
     * @param finalSolutionId
     */
    private void generateParFile(Integer finalSolutionId) {
        String filePath = path + File.separator + "question" + finalSolutionId + ".par";
        try (FileWriter fw = new FileWriter(filePath)) {
            fw.write("");//清空数据
            for (String str : config) {
                fw.append(str + "\r\n");
            }
            fw.append("PROBLEM_FILE = question" + finalSolutionId + ".tsp" + "\r\n");
            fw.append("TOUR_FILE = ./output" + finalSolutionId + ".txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @description : 构建tsp问题文件
     */
    private void generateProblemFile(List<NodePojo> nodes, Integer finalSolutionId) {
        String filePath = path + File.separator + "question" + finalSolutionId + ".tsp";
        try (FileWriter fw = new FileWriter(filePath)) {
            fw.write("");//清空数据
            fw.append("NAME : solution" + finalSolutionId + "\r\n");
            fw.append("COMMENT : route plan problem (smallsmart)" + new Date().toString() + "\r\n");
            fw.append("TYPE : TSP" + "\r\n");
            fw.append("DIMENSION : " + nodes.size() + "\r\n");
            fw.append("EDGE_WEIGHT_TYPE : GEO" + "\r\n");
            fw.append("NODE_COORD_SECTION" + "\r\n");
            for (int i = 0; i < nodes.size(); i++) {
                fw.append(i + 1 + " " + nodes.get(i).getLat() + " " + nodes.get(i).getLng());
                fw.append("\r\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行LKH算法
     *
     * @param finalSolutionId
     */
    private void runLKH(Integer finalSolutionId) {
        BufferedReader br = null;
        BufferedReader brError = null;
        try { // 运行LKH.exe
            ProcessBuilder processBuilder = new ProcessBuilder(path + File.separator + "LKH", "question" + finalSolutionId + ".par");
            File dir = new File(path);
            processBuilder.directory(dir);
            Process p = processBuilder.start();
            String line = null;
            //获得子进程的输入流。
            br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            brError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            p.getOutputStream().close(); // 关闭输出流防止程序挂起无法结束
            while ((line = br.readLine()) != null || (line = brError.readLine()) != null) {
                //输出exe输出的信息以及错误信息
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private List<Solution> readResultFile(List<NodePojo> nodes, VehicleMessage vehicle, Integer finalSolutionId) throws ServerException {
        String filePath = path + File.separator + "output" + finalSolutionId + ".txt";
        List<Solution> res = new ArrayList<>();
        Solution solution = new Solution();
        solution.setVehicleId(vehicle.getVehicleId());
        solution.setFinalSolutionId(finalSolutionId);
        List<NodePojo> routeList = new ArrayList<>(nodes.size());
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                if (NumberUtils.isNumber(line)) {
                    Integer index = NumberUtils.createInteger(line);
                    if (index == -1) {
                        routeList.add(nodes.get(0));
                        break;
                    }
                    NodePojo node = nodes.get(index - 1);
                    routeList.add(node);
                }
            }
        } catch (IOException e) {
            throw new ServerException("读取结果文件异常");
        }
        String route = JSON.toJSONString(routeList);
        solution.setRoute(route);
        res.add(solution);
        return res;
    }

}
